//
//  EAGLView.mm
//  ___PROJECTNAME___
//
//  Created by ___FULLUSERNAME___ on ___DATE___.
//  Copyright ___ORGANIZATIONNAME___ ___YEAR___. All rights reserved.
//

//File modified by Goffredo Marocchi (this is not the original SIO2 provided file,
//sio2 and its creator ROm are not responsible for the content of this file as it is now


#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/EAGLDrawable.h>

#import "EAGLView.h"
#import "ES1Renderer.h"

#import "___PROJECTNAMEASIDENTIFIER___AppDelegate.h"
#import "main.h"
#import "___PROJECTNAMEASIDENTIFIER___template.h"
#import "SysTools.h"

id sio2GLView = nil;
extern SIO2* sio2Ptr;
extern int projPush;
extern int projPop;

// A class extension to declare private methods
@interface EAGLView () 

@end

@implementation EAGLView

@dynamic animationFrameInterval;
@synthesize shadowMatrix, sceneProjector, updateCameraProjectionMatrix, updateCameraModelViewMatrix;

// You must implement this
+ (Class)layerClass {
	return [CAEAGLLayer class];
}


//The GL view is stored in the nib file. When it's unarchived it's sent -initWithCoder:
- (id)initWithCoder:(NSCoder*)coder {
	
    if ((self = [super initWithCoder:coder]))
    {
        // Get the layer
        CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
		
        eaglLayer.opaque = YES;
        eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
                                        [NSNumber numberWithBool:FALSE], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
		
        renderer = [[ES1Renderer alloc] init];
		if (!renderer)
		{
			[self release];
			return nil;
        }
		
		animationFrameInterval = 1;
		
		displayLink = nil;
		animationTimer = nil;
		
		displayLinkSupported = DISPLAY_LINK;
		
		isAnimating = FALSE;
		updateCameraModelViewMatrix = TRUE;
		updateCameraProjectionMatrix = TRUE;
		
		shadowMatrix = [ShadowMatrix alloc];
		sceneProjector = [[Projector alloc] init];
		
		SET_VEC3(0.0f, 0.0f, 0.0f,&temp_camera_loc);
		SET_VEC3(0.0f, 0.0f, 0.0f,&temp_camera_target);
		
		isSimulator = ( 0 == sio2StringCmp((const char*)"Apple Software Renderer", 
										   (const char*) glGetString(GL_RENDERER)) )?TRUE:FALSE;
		
		printf("\nWe are running on the iOS %s\n", (isSimulator?"simulator":"device"));
		
	}
	sio2GLView = self;
	return self;
}

- (void)drawView:(id)sender {
	//TODO: check potential side-effects of the "optimization" below.
	
	//forces the app to finish work in the runloop before rendering the frame
	//[[NSRunLoop currentRunLoop] runUntilDate: [NSDate distantPast]]; 
	
	//
	[renderer renderSIO2:displayLink];
}

-(CFTimeInterval) getCADLTimeStamp {
	if (DISPLAY_LINK) return ((CADisplayLink *)displayLink).timestamp;
	else return -1.0f;
}

- (void)layoutSubviews {
    [renderer resizeFromLayer:(CAEAGLLayer*)self.layer];
    [self drawView:nil];
}

- (NSInteger)animationFrameInterval
{
    return animationFrameInterval;
}

- (void)setAnimationFrameInterval:(NSInteger)frameInterval
{
    // Frame interval defines how many display frames must pass between each time the
    // display link fires. The display link will only fire 30 times a second when the
    // frame internal is two on a display that refreshes 60 times a second. The default
    // frame interval setting of one will fire 60 times a second when the display refreshes
    // at 60 times a second. A frame interval setting of less than one results in undefined
    // behavior.
    if (frameInterval >= 1)
    {
        animationFrameInterval = frameInterval;
		
        if (isAnimating)
        {
            [self stopAnimation];
            [self startAnimation];
        }
    }
}

- (void)startAnimation {
    if (!isAnimating) {
        if (displayLinkSupported) {
            // CADisplayLink is API new to iPhone SDK 3.1. Compiling against earlier versions will result in a warning, but can be dismissed
            // if the system version runtime check for CADisplayLink exists in -initWithCoder:. The runtime check ensures this code will
            // not be called in system versions earlier than 3.1.
			
            displayLink = [NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self selector:@selector(drawView:)];
            [displayLink setFrameInterval:animationFrameInterval];
            [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
        }
        else {
            animationTimer = [NSTimer scheduledTimerWithTimeInterval:(NSTimeInterval)((1.0 / 60.0) * animationFrameInterval) target:self
															selector:@selector(drawView:) userInfo:nil repeats:TRUE];
		}
		
        isAnimating = TRUE;
    }
}

- (void)stopAnimation {
    if (isAnimating) {
        if (displayLinkSupported) {
            [displayLink invalidate];
            displayLink = nil;
        }
        else {
            [animationTimer invalidate];
            animationTimer = nil;
        }
        isAnimating = FALSE;
    }
}

- (void)dealloc {
	[shadowMatrix release];
	[renderer release];	
	
	[super dealloc];
}

-(int)  getTriangleCountFromObject:(SIO2object*)obj {
	
	int nVG=0;
	int nVertices=0;
	int nTriangles=0;
	
	nVG = obj->n_vgroup;
	NSAssert(nVG>0, @"(!) The number of vertex groups must be greater than 0 (!)");
	//DPRINTF("Number of vertex groups = %i\n", nVG);
	
	//obj->buf = (unsigned char *)sio2MapBuffer( obj->vbo, GL_ARRAY_BUFFER );
	
	for (int i=0; i<nVG; i++) {
		if (obj->_SIO2vertexgroup[i]->mode == GL_TRIANGLES) {
			
			nVertices += obj->_SIO2vertexgroup[i]->n_ind;
			
		}
	}
	
	nTriangles = (nVertices/3);
	//DPRINTF("Triangle count for this object is: %i\n", nTriangles);
	//obj->buf = (unsigned char *) sio2UnmapBuffer(obj->vbo, GL_ARRAY_BUFFER);
	
	return nTriangles;
}

-(void) getTriangleList:(vec3*)trianglePtr fromObject:(SIO2object*)obj {
	
	int i, j, triangleId;
	int maxIndex;
	
	i = 0;
	j = 0;
	triangleId = 0;
	
	obj->buf = (unsigned char *)sio2MapBuffer( obj->vbo, GL_ARRAY_BUFFER );
	
	for (i=0; i<obj->n_vgroup; i++) {
		if (obj->_SIO2vertexgroup[i]->mode == GL_TRIANGLES) {
			obj->_SIO2vertexgroup[ i ]->ind = ( unsigned short *)sio2MapBuffer( obj->_SIO2vertexgroup[i]->vbo,
																			   GL_ELEMENT_ARRAY_BUFFER );
			maxIndex = obj->_SIO2vertexgroup[i]->n_ind;
			
			//DPRINTF ("maxIndex = %i\n", maxIndex);
			
			j=0;
			while (j < maxIndex) {
				
				triangleId = (i*maxIndex)+j;
				
				int idV0 = obj->_SIO2vertexgroup[ i ]->ind[j];
				int idV1 = obj->_SIO2vertexgroup[ i ]->ind[j+1];
				int idV2 = obj->_SIO2vertexgroup[ i ]->ind[j+2];
				
				//DPRINTF("index[%i] = %i\n", j, idV0);
				//DPRINTF("index[%i] = %i\n", j+1, idV1);
				//DPRINTF("index[%i] = %i\n", j+2, idV2);
				
				memcpy( &trianglePtr[triangleId], &obj->buf[ idV0 * 12 ], 12 );
				memcpy( &trianglePtr[triangleId+1], &obj->buf[ idV1 * 12 ], 12 );
				memcpy( &trianglePtr[triangleId+2], &obj->buf[ idV2 * 12 ], 12 );
				
				sio2Vec3Add(&trianglePtr[triangleId], obj->_SIO2transform->loc, &trianglePtr[triangleId]);
				sio2Vec3Add(&trianglePtr[triangleId+1], obj->_SIO2transform->loc, &trianglePtr[triangleId+1]);
				sio2Vec3Add(&trianglePtr[triangleId+2], obj->_SIO2transform->loc, &trianglePtr[triangleId+2]);
				
#if DEBUG==1
				//PRINT_SVEC("obj->_SIO2transform->loc is: ", *obj->_SIO2transform->loc);
				//PRINT_SVEC("Vertex is: ", trianglePtr[triangleId]);
				//PRINT_SVEC("Vertex is: ", trianglePtr[triangleId+1]);
				//PRINT_SVEC("Vertex is: ", trianglePtr[triangleId+2]);
#endif
				
				j += 3;
			} 
			
			obj->_SIO2vertexgroup[ i ]->ind = ( unsigned short *) sio2UnmapBuffer( obj->_SIO2vertexgroup[i]->vbo, 
																				  GL_ELEMENT_ARRAY_BUFFER );   
		}
	}
	
	obj->buf = (unsigned char *) sio2UnmapBuffer(obj->vbo, GL_ARRAY_BUFFER);
	return;
	
}

-(void) calculatePlaneNormal:(SIO2object *)plane withCam:(const SIO2camera*)cam withNormal:(vec3*)normal {
	
	int nTriangles = [self getTriangleCountFromObject:plane];
	
	vec3* vertexListArr = (vec3*)calloc((nTriangles*3), 12); //sizeof(vec3) == 12
	
	vec3* vertexList = vertexListArr;
	NSAssert (vertexList != NULL, @"(!)calloc failed(!)");
	
	[self getTriangleList:vertexList fromObject:plane];
	
	vec3 normal_vec;
	for (int i=0; i<nTriangles; i++) {
		
		//DPRINTF("calculating normal...");
		[self calculateTriangleNormal:&normal_vec v0:&vertexList[i*nTriangles]
								   v1:&vertexList[(i*nTriangles)+1] v2:&vertexList[(i*nTriangles)+2]];
		
		vertexList += 3;
		
		if (sio2DotProduct(&normal_vec, (vec3*)&upWorld) > 0.0f) {
			COPYVEC3 (&normal_vec, normal);
#if DEBUG==1			
			//NSLog(@"\n(OK) Found the plane's normal vector");
			PRINT_VEC(*normal);
			//printf("normal's lenght = %f\n", sio2Magnitude(normal));
			//printf("\n");
#endif
			break;
		}
	}
	
	SAFE_FREE(vertexListArr);
	return;
}

-(void) calculateTriangleNormal:(vec3*)normal v0:(const vec3*)v0 v1:(const vec3*)v1 v2:(const vec3*)v2  {
	
	vec3 u;
	vec3 v;
	vec3 temp;
#if DEBUG==1
	//PRINT_SVEC ("v0 = ",*v0);
	//PRINT_SVEC ("v1 = ",*v1);
	//PRINT_SVEC ("v2 = ",*v2);
#endif
	
	sio2Vec3Diff((vec3*)v1, (vec3*)v0, &u);
	sio2Vec3Diff((vec3*)v2, (vec3*)v0, &v);
	
#if DEBUG==1
	//PRINT_SVEC ("u = ",u);
	//PRINT_SVEC ("v = ",v);
#endif
	
	sio2CrossProduct(&u, &v, &temp);
	sio2Normalize(&temp, &temp);
	
#if DEBUG==1
	//PRINT_SVEC("normal: ", temp);
#endif
	
	COPYVEC3 (&temp, normal);
	
	return;
	
}

-(void) matLookAt:(MATRIX*)m eye:(vec3*)_e vTarget:(vec3*)_c upVector:(vec3*)_u {
	
	vec3 dir;
	sio2Vec3Diff(_c, _e, &dir);
	[self matLookAt:m eye:_e vDir:&dir upVector:_u];
	return;
}

-(void) matLookAt:(MATRIX*)m eye:(vec3*)_e vDir:(vec3*)_d upVector:(vec3*)_u {
	vec3 f,
	s,
	u;
	
	MATRIX matTemp;
	
	memset( m->f, 0, 64 );
	
	sio2Normalize( _d, &f );
	sio2CrossProduct( &f, _u, &s );
	sio2Normalize( &s, &s );
	sio2CrossProduct( &s, &f, &u );
	
    m->f[ 0  ] = s.x;
    m->f[ 4  ] = s.y;
    m->f[ 8  ] = s.z;
	
    m->f[ 1  ] = u.x;
    m->f[ 5  ] = u.y;
    m->f[ 9  ] = u.z;
	
    m->f[ 2  ] = -f.x;
    m->f[ 6  ] = -f.y;
    m->f[ 10 ] = -f.z;
	
	m->f[ 15 ] = 1.0f;
	
	MatrixTranslation(matTemp, -_e->x, -_e->y, -_e->z );
	MatrixMultiply((*m), matTemp, (*m)); //be careful of the order... 
	//SIO2's way: glRotate(for landscape) --> glMultMatrix (View matrix thanks to lookAt) --> glTranslate(-eye);
	//...OpenGL uses a matrix stack... so, be careful with the order of multiplications if you do them by hand...
	//also, Oolong's MatrixMultiply functions treat matrices as if they were row major matrices
}

-(void) matPerspective:(float*)m withFov:(float)fovYdeg aspectRatio:(float)aRatio zNear:(float)zN zFar:(float)zF portrait:(BOOL)rot 
				zRangeInverted:(BOOL)inverted{
	float s,
	c,
	d = zF - zN,
	r = fovYdeg * 0.5f * SIO2_DEG_TO_RAD; //half of the view frustum vertical fov angle
	
	s = sinf( r );
	c = cosf( r ) / s;// c = 1/tan(r) = 1/(sin(r)/cos(r)) = cos(r)/sin(r) = cotan(r)
	
	memset( &m[ 0 ], 0, 64 );
	
	m[ 0  ] = c / aRatio;
	m[ 5  ] = c;
	m[ 10 ] = -( zF + zN ) / d;
	m[ 11 ] = -1.0f;
	m[ 14 ] = -2.0f * zF * zN / d;
	
	if(inverted){
		m[ 10 ] = ( zF + zN ) / d;
		m[ 14 ] = 2.0f * zF * zN / d;
	}
	if(rot){
		m[0] = 0;	m[4]=-c / aRatio;	m[8]=0;	m[12]=0;
		m[1] = c / aRatio;	m[5]=0;	m[9]=0;	m[13]=0;
	}
}

/*!***************************************************************************
 @Function		Perspective
 @Input			fovYdeg		vertical view angle
 @Input			nearPlane	near clipping distance
 @Input			rot			is the viewport in landscape mode or portrait
 @Input			inverted	is the depth range inverted or not
 @Returns		Perspective matrix
 @Description	perspective calculation where far plane is assumed to be at an infinite distance and the screen
 space Z is inverted
 *****************************************************************************/
-(void) matPerspectiveFPz:(float*)m withFov:(float)fovYdeg aspectRatio:(float)aRatio zNear:(float)zN portrait:(BOOL)rot 
				   zRangeInverted:(BOOL)inverted{		
	float s,
	c,
	r = fovYdeg * 0.5f * SIO2_DEG_TO_RAD; //half of the view frustum vertical fov angle
	
	s = sinf( r );
	c = cosf( r ) / s;// c = 1/tan(r) = 1/(sin(r)/cos(r)) = cos(r)/sin(r) = cotan(r)
	
	memset( &m[ 0 ], 0, 64 );
	
	m[ 0  ] = c / aRatio;
	m[ 5  ] = c;
	m[ 10 ] = -1.0f;
	m[ 11 ] = -1.0f;
	m[ 14 ] = -2.0f * zN;
	
	if(inverted){
		m[ 10 ] = 1.0f;
		m[ 14 ] = 2.0f * zN;
	}
	if(rot){
		m[0] = 0;	m[4]=-c / aRatio;	m[8]=0;	m[12]=0;
		m[1] = c / aRatio;	m[5]=0;	m[9]=0;	m[13]=0;
	}
}

/*!***************************************************************************
 @Function			vec3Lerp
 @Output			vOut	Result of the interpolation
 @Input				v1		First vector to interpolate from
 @Input				v2		Second vector to interpolate form
 @Input				s		Coefficient of interpolation
 @Description		This function performs the linear interpolation based on
 the following formula: V1 + s(V2-V1).
 *****************************************************************************/
-(void) vec3LerpVec1:(vec3*)v1 vec2:(vec3*)v2 withCoeff:(float)s intoVec:(vec3*)vOut {
	vOut->x = v1->x + s * (v2->x - v1->x);
	vOut->y = v1->y + s * (v2->y - v1->y);
	vOut->z = v1->z + s * (v2->z - v1->z);
}

-(void) setupLighting {
	unsigned int i = 0;
	//static float angle = 0.0f;
	while( i < sio2->_SIO2resource->n_lamp )
	{
		// Convert directly the void* to a SIO2lamp handle.
		SIO2lamp *_SIO2lamp = ( SIO2lamp * )sio2->_SIO2resource->_SIO2lamp[ i ];
		// Set the color to our current lamp
		glColor4f( _SIO2lamp->col->x,
				  _SIO2lamp->col->y,
				  _SIO2lamp->col->z,
				  _SIO2lamp->col->w );
		++i;
	}
}

-(void) loadSIO2Camera:(const char*)camera {
	sio2->_SIO2camera = ( SIO2camera * )sio2ResourceGet( sio2->_SIO2resource,
														SIO2_CAMERA,
														(char*)camera);
	[self setUpdateCameraProjectionMatrix:TRUE];
}

-(void) cameraUpdate:(SIO2camera*) _SIO2camera {
	//TODO: this needs to be overhauled for SIO2 v2.0
	vec3 v;
	
	if( _SIO2camera->_SIO2ipo && _SIO2camera->_SIO2ipo->state == SIO2_PLAY )
	{
		sio2TransformSetLoc( _SIO2camera->_SIO2transform,
							_SIO2camera->_SIO2ipo->_SIO2transform->loc );
		
		sio2TransformSetRot( _SIO2camera->_SIO2transform,
							_SIO2camera->_SIO2ipo->_SIO2transform->rot );
		
		sio2Rotate3D( _SIO2camera->_SIO2transform->loc,
					 90.0f - _SIO2camera->_SIO2ipo->_SIO2transform->rot->x,
					 _SIO2camera->_SIO2ipo->_SIO2transform->rot->z,
					 -1.0f,
					 &v );
		
		sio2Vec3Diff( &v,
					 _SIO2camera->_SIO2transform->loc,
					 _SIO2camera->_SIO2transform->dir );
	}
	
	else if( _SIO2camera->_btRigidBody )
	{
		btTransform _btTransform;
		
		_SIO2camera->_btRigidBody->getWorldTransform().getOpenGLMatrix( _SIO2camera->_SIO2transform->mat );
		
		sio2TransformGetLocFromMatrix( _SIO2camera->_SIO2transform );
		
		sio2TransformGetRotFromMatrix( _SIO2camera->_SIO2transform );
		
		_SIO2camera->_SIO2transform->mat[ 12 ] =
		_SIO2camera->_SIO2transform->mat[ 13 ] =
		_SIO2camera->_SIO2transform->mat[ 14 ] = 0.0f;						
	}
	
	vec3 target;
	sio2Vec3Add(_SIO2camera->_SIO2transform->loc, _SIO2camera->_SIO2transform->dir, &target);
	
	if (_SIO2camera->_SIO2transform->_SIO2parent) {
		SIO2transform *ptr = ( SIO2transform * )_SIO2camera->_SIO2transform->_SIO2parent; 
		glMultMatrixf( &ptr->mat[ 0 ] );
	}	
	
	//LookAt calculations should follow...
}

//Camera setup for landscape: 
//order submitted: PROJECTION_MATRIX (Perspective) --> VIEW_MATRIX (lookAt) --> MODEL_MATRIX 
//order of evaluation: modelToWorld --> worldToCamera (view) --> CameraToHomogeneous/Post-Projection (proj)
-(void) renderCameraProjectionMatrix:(MATRIX*)mProjection {
	if (updateCameraProjectionMatrix) {
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glMultMatrixf(&mProjection->f[0]);
		updateCameraProjectionMatrix = FALSE;
	}
}

-(void) renderCameraModelViewMatrix:(MATRIX*)mView enableLandscape:(BOOL)landscape {
	if (updateCameraModelViewMatrix) {
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		
		if(landscape) {
			glPushMatrix();
			GL_ROTATE_TO_LANDSCAPE();
		}
		
		//equivalent to sio2CameraRender...
		//TODO: cameraUpdate needs to be overhauled for SIO2 v2.0
		{
			[self cameraUpdate:sio2->_SIO2camera];
			glMultMatrixf(&mView->f[0]); //apply lookAt calculated view matrix
		}
		
		sio2CameraGetModelviewMatrix( sio2->_SIO2camera );
		sio2CameraGetProjectionMatrix( sio2->_SIO2camera );
		sio2CameraUpdateFrustum(sio2->_SIO2camera); 
		sio2ResourceCull(sio2->_SIO2resource,sio2->_SIO2camera);
		
		updateCameraModelViewMatrix = FALSE;
	}
}

-(void) drawCameraFrustum:(SIO2camera*)cam aspectRatio:(float)aspect {
#if DEBUG==1
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	{
		glEnableClientState(GL_VERTEX_ARRAY);
		
		float nearLeft, nearRight, nearBottom, nearTop, farLeft, farRight, farBottom, farTop;
		float nearZ = cam->cstart;
		float farZ = cam->cend / 300.0f;
		
		nearTop = 1.0f * (nearZ+0.0f) * tanf(cam->fov * 0.5f * SIO2_DEG_TO_RAD);
		nearBottom = -nearTop;
		farTop = 1.0f * farZ * tanf(cam->fov * 0.5f * SIO2_DEG_TO_RAD);
		farBottom = -farTop;
		
		nearLeft = 1.0f *nearBottom * aspect;
		nearRight = 1.0f * nearTop * aspect;	
		farLeft = farBottom * aspect;
		farRight = farTop * aspect;	
		
		SIO2transform* t = sio2TransformInit();
		
		vec3 cameraDir = *cam->_SIO2transform->dir;
		vec3 cameraRot = *cam->_SIO2transform->rot;
		
		PRINT_VEC(cameraDir);
		
		MUL_FVEC3 (-1, &cameraDir);
		
		PRINT_VEC(cameraDir);
		
		//MUL_FVEC3 (-1, &cameraRot);
		
		sio2TransformSetDir(t, &cameraDir);
		//sio2TransformSetRot(t,  &cameraRot);
		sio2TransformSetScl(t, cam->_SIO2transform->scl);
		sio2TransformSetLoc(t, cam->_SIO2transform->loc);
		
		sio2TransformApply(t);
		
		const GLfloat v[8*3] = {
			nearLeft, nearTop,  nearZ,
			nearRight, nearTop, nearZ,
			nearRight, nearBottom, nearZ,
			nearLeft, nearBottom, nearZ,
			farLeft, farTop, farZ,
			farRight, farTop, farZ,
			farRight, farBottom, farZ,
			farLeft, farBottom, farZ
		};//frustum vertices
		const GLshort indices[] = 
		{0,1,2,3,0,
			4,5,6,7,4,
			0,4,7,3,0,
			1,5,6,2,1};
		
		glEnableClientState(GL_LINE_STRIP);
		
		glVertexPointer(3, GL_FLOAT, 0, &v[0]);
		glDrawElements(GL_LINE_STRIP, 20, GL_UNSIGNED_SHORT, &indices[0]);
		
		glDisableClientState(GL_LINE_STRIP);
		glDisableClientState(GL_VERTEX_ARRAY);
	}
	glPopMatrix();	
#endif
}

-(void) renderFromViewPoint:(vec3*)o withTarget:(vec3*)t {
	
	//TODO: implement the method
	
}

-(void) enableClipPlane:(int)clipPlaneIndex withPlaneNormal:(vec3*)normal withOffset:(float)offset {
    glEnable(GL_CLIP_PLANE0 + clipPlaneIndex);
    GLfloat planeCoefficients[] = {normal->x, normal->y, normal->z, offset}; 
    glClipPlanef(GL_CLIP_PLANE0 + clipPlaneIndex, planeCoefficients);
}

-(void) disableClipPlane:(int)clipPlaneIndex {
	glDisable(GL_CLIP_PLANE0 + clipPlaneIndex);
}

-(void) renderAllObjects {	
	//TODO: a list of objects which are neither shadow casters nor shadow receivers might be useful
	
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	
	int i = 0;
	while( i < sio2Ptr->_SIO2resource->n_object )
	{
		SIO2object *obj = ( SIO2object * )sio2Ptr->_SIO2resource->_SIO2object[ i ];
		int _mask = SIO2_LAMP | SIO2_OBJECT_SOLID | SIO2_OBJECT_SHADOW;
		if( (obj->type & SIO2_OBJECT_SOLID ) && obj->dst )
		{
			sio2ObjectRender( obj,
							 sio2Ptr->_SIO2window,			
							 sio2Ptr->_SIO2camera,
							 !( _mask & SIO2_RENDER_NO_MATERIAL ),
							 !( _mask & SIO2_RENDER_NO_MATRIX ) );
		}
		i++;
	}
	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	
	SIO2_SOFT_RESET();
}

-(void) renderShadowReceiverObject:(SIO2object*)obj {
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	
	int _mask = SIO2_RENDER_SOLID_OBJECT | SIO2_LAMP;
	if( (obj->type & SIO2_OBJECT_SOLID ) && obj->dst )
	{
		//DPRINTF ("Object's name (shadow receiver) %s\n", obj->name);
		sio2ObjectRender( obj,
						 sio2->_SIO2window,			
						 sio2->_SIO2camera,
						 !( _mask & SIO2_RENDER_NO_MATERIAL ),
						 !( _mask & SIO2_RENDER_NO_MATRIX ) );
	}
	
	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	
	SIO2_SOFT_RESET();
}

-(void) renderShadowCasters {
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	
	int i = 0;
	int _mask = SIO2_RENDER_SOLID_OBJECT | SIO2_LAMP ;
	{
		// Render our shadow objects
		while( i < sio2->_SIO2resource->n_object )
		{
			BOOL continueLoop = FALSE;
			SIO2object *_SIO2object = ( SIO2object * )sio2->_SIO2resource->_SIO2object[ i ];
			if(sio2IsStateEnabled( _SIO2object->flags, SIO2_OBJECT_SHADOW) || TRUE)
			{
				for (int j=0; j<MAX_SHADOW_RECEIVERS; j++) {
					if (objShadowReceiver[j] == NULL) { 
						break;
					}
					
					if (0==sio2StringCmp(_SIO2object->name, objShadowReceiver[j]->name)) {
						continueLoop = TRUE;
						//DPRINTF ("\nskipping Object %s\n", _SIO2object->name);
						break;
					}
				}
				if(continueLoop) {
					++i;
					continue;
				}
				
				//DPRINTF ("Object's name %s\n", _SIO2object->name);
				if( (_SIO2object->type & SIO2_OBJECT_SOLID ) && _SIO2object->dst )
				{
					sio2ObjectRender( _SIO2object,
									 sio2->_SIO2window,			
									 sio2->_SIO2camera,
									 !( _mask & SIO2_RENDER_NO_MATERIAL ),
									 !( _mask & SIO2_RENDER_NO_MATRIX ) );
				}
			}
			++i;
		}
	}
	
	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	
	SIO2_SOFT_RESET();
}

-(void) renderLights{
	unsigned char index = 0;
	
	//sio2LampReset();
	
	int i = 0;
	while( i < sio2Ptr->_SIO2resource->n_lamp )
	{
		SIO2lamp *_SIO2lamp = ( SIO2lamp * )sio2Ptr->_SIO2resource->_SIO2lamp[ i ];
		
		if( sio2LampRender( _SIO2lamp, index ) )
		{ ++index; }
		
		if( index == ( SIO2_LAMP_MAX - 1 ) )
		{ break; }
		
		++i;
	}
}

-(void) renderTransparentObjects {
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	
	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	
	SIO2_SOFT_RESET();
}

-(void) renderIPO {
	int i = 0;
	while( i != sio2Ptr->_SIO2resource->n_ipo )
	{
		SIO2ipo *_SIO2ipo = ( SIO2ipo * )sio2Ptr->_SIO2resource->_SIO2ipo[ i ];
		
		if( _SIO2ipo->state == SIO2_PLAY )
		{ sio2IpoRender( _SIO2ipo, sio2Ptr->_SIO2window ); }	
		
		++i;
	}	
}

-(void) renderSIO2ObjectNormal:(char*) objString withSIO2:(SIO2*)sio2 {
	
	SIO2object* _plane = (SIO2object *)sio2ResourceGet(sio2->_SIO2resource, SIO2_OBJECT, objString);
	
	//glLoadIdentity();
	//sio2TransformRender(_plane->_SIO2transform);
	
	vec3 nPlane; 
	vec3* planePoint = _plane->_SIO2transform->loc;
	[self calculatePlaneNormal:_plane withCam:sio2->_SIO2camera withNormal:&nPlane];
	
#if DEBUG==1
	PRINT_SVEC("Camera location in SIO2: ", *sio2->_SIO2camera->_SIO2transform->loc);
#endif
	
	//vec3 cam = {77.906f, -72.179f, 55.299f};
	//PRINT_SVEC("Camera location in Blender: ", cam);
	
	[self drawNormal:&nPlane ofPoint:planePoint];
}

-(void) drawNormal:(vec3*)normal ofPoint:(vec3*)p {
	
	GLubyte yellowColor[] = {0, 255, 255, 255,
		0, 255, 255, 255};
	
	vec3 p2 = [self travelAlongNormal:normal fromPoint:p distance:15.0f];
	[self drawLineFrom:p to:&p2 withColor:&yellowColor[0]];
	
	/*GLubyte color2 []= {255, 0, 0, 255,
	 255, 0, 0, 255};
	 p2 = [self travelAlongNormal:normal fromPoint:p distance:-100.0f];
	 [self drawLineFrom:p to:&p2 withColor:&color2[0]];*/
	
}

-(vec3) travelAlongNormal:(vec3*)normal fromPoint:(vec3*)p1 distance:(float)dist {
	
	vec3 p2;
	vec3 temp = *normal;
	
	MUL_FVEC3(dist, &temp);
	
	//PRINT_SVEC("old position = ", *p1); 
	
	//DPRINTF ("distance = %f", dist);
	//PRINT_SVEC("normal vector * distance = ", temp); 
	
	sio2Vec3Add(p1, &temp, &p2);
	
	//PRINT_SVEC("new position = ", p2); 
	
	return p2;
	
}

-(void) drawLineFrom:(vec3 *)fromPoint to:(vec3 *)toPoint withColor:(GLubyte *)color {
	
#if DEBUG==1
	GLfloat tmp[] = { fromPoint->x, fromPoint->y, fromPoint->z,
		toPoint->x, toPoint->y, toPoint->z };
	
	glEnableClientState(GL_COLOR_ARRAY);
	glEnableClientState(GL_VERTEX_ARRAY);
	
	glColorPointer( 4, GL_UNSIGNED_BYTE, 0, color);
	glVertexPointer( 3,	GL_FLOAT, 0, &tmp[0]);
	
	glEnableClientState( GL_POINTS);
	glEnableClientState(GL_LINES);
	
	glPointSize( 10.0f );
	glLineWidth(2.5f);
	glDrawArrays( GL_POINTS, 0, 2 );
	glDrawArrays( GL_LINES, 0, 2 );
	
	glDisableClientState(GL_POINTS);
	glDisableClientState(GL_LINES);
	
	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_COLOR_ARRAY);
	
#endif
}

-(id) getDelegate {
	___PROJECTNAMEASIDENTIFIER___AppDelegate* mainDelegate = (___PROJECTNAMEASIDENTIFIER___AppDelegate*)[[UIApplication sharedApplication] delegate];
	return mainDelegate;
}

- (CGSize)frame_size { return [self frame].size;}

-(void) loadImageSetTexture:(NSString*)image withId:(NSString*)texId {
	
	SIO2image *myImage = sio2ImageInit( [texId UTF8String] );
	SIO2stream *_SIO2stream    = sio2StreamOpen((char*)[image UTF8String], 1 );
	{
		sio2ImageLoad( myImage, _SIO2stream );
		sio2ImageGenId( myImage, SIO2_IMAGE_BILINEAR, 0.0f );
	}
	_SIO2stream = sio2StreamClose( _SIO2stream );
	
}

-(void) setUpdateCameraProjectionMatrix:(BOOL)value {
	if (value) {
		updateCameraProjectionMatrix = TRUE;
		updateCameraModelViewMatrix = TRUE;
	}
	else updateCameraModelViewMatrix = value;
}

-(void) setObjShadowReceiver:(SIO2object **)obj {
	for (int i = 0; i < MAX_SHADOW_RECEIVERS; i++) {
		objShadowReceiver[i] = obj[i];
	}
	[shadowMatrix setObjShadowReceiver:obj];
}

+(BOOL) isSIO2ObjectName:(NSString*)str InSIO2ObjectArray:(SIO2object**)obj ofSize:(int)size {
	BOOL result = FALSE;
	
	if (!obj) return FALSE;
	
	LOCAL_AR_POOL_ALLOC();
	for (int i = 0; i<size; i++) {
		if (NULL == obj[i]) {
			break;
		}
		NSString* temp = [NSString stringWithUTF8String:obj[i]->name];
		if ([str isEqualToString:temp]) {
			result = TRUE;
			break;
		}
	}
	LOCAL_AR_POOL_DRAIN();
	
	return result;
}

@end
